using System.IO;
using System.IO.Abstractions;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Shouldly;
using Spectre.Console;
using Stryker.Abstractions;
using Stryker.Abstractions.Options;
using Stryker.CLI.Clients;
using Stryker.CLI.Logging;
using Stryker.Configuration;
using Stryker.Core;
using Stryker.Core.Initialisation;

namespace Stryker.CLI.UnitTest;

[TestClass]
public class FileConfigReaderTests
{
    [TestMethod]
    public void WithNoArgumentsAndNoConfigFile_ShouldStartStrykerWithConfigOptions()
    {
        var mock = new Mock<IStrykerRunner>(MockBehavior.Strict);
        var options = new StrykerOptions()
        {
            Thresholds = new Thresholds()
            {
                High = 80,
                Low = 60,
                Break = 0
            }
        };
        var currentDirectory = Directory.GetCurrentDirectory();
        Directory.SetCurrentDirectory($"..{Path.DirectorySeparatorChar}");
        var runResults = new StrykerRunResult(options, 0.3);
        mock.Setup(x => x.RunMutationTest(It.IsAny<IStrykerInputs>())).Returns(runResults).Verifiable();
        var target = new StrykerCli(mock.Object, new ConfigBuilder(), Mock.Of<ILoggingInitializer>(), Mock.Of<IStrykerNugetFeedClient>(), Mock.Of<IAnsiConsole>(), Mock.Of<IFileSystem>());

        target.Run(new string[] { });

        mock.VerifyAll();

        Directory.SetCurrentDirectory(currentDirectory);
    }

    [TestMethod]
    [DataRow("--config-file")]
    [DataRow("-f")]
    public void WithJsonConfigFile_ShouldStartStrykerWithConfigFileOptions(string argName)
    {
        IStrykerInputs actualInputs = null;
        var options = new StrykerOptions()
        {
            Thresholds = new Thresholds()
            {
                High = 80,
                Low = 60,
                Break = 0
            }
        };
        var runResults = new StrykerRunResult(options, 0.3);

        var mock = new Mock<IStrykerRunner>(MockBehavior.Strict);
        mock.Setup(x => x.RunMutationTest(It.IsAny<IStrykerInputs>()))
            .Callback<IStrykerInputs>(c => actualInputs = c)
            .Returns(runResults)
            .Verifiable();

        var target = new StrykerCli(mock.Object, new ConfigBuilder(), Mock.Of<ILoggingInitializer>(), Mock.Of<IStrykerNugetFeedClient>(), Mock.Of<IAnsiConsole>(), Mock.Of<IFileSystem>());

        target.Run(new string[] { argName, "filled-stryker-config.json" });

        mock.VerifyAll();

        actualInputs.AdditionalTimeoutInput.SuppliedInput.ShouldBe(9999);
        actualInputs.VerbosityInput.SuppliedInput.ShouldBe("trace");
        actualInputs.SourceProjectNameInput.SuppliedInput.ShouldBe("ExampleProject.csproj");
        actualInputs.ReportersInput.SuppliedInput.ShouldHaveSingleItem();
        actualInputs.ReportersInput.SuppliedInput.ShouldContain(Reporter.Json.ToString());
        actualInputs.ConcurrencyInput.SuppliedInput.ShouldBe(1);
        actualInputs.ThresholdBreakInput.SuppliedInput.ShouldBe(20);
        actualInputs.ThresholdLowInput.SuppliedInput.ShouldBe(30);
        actualInputs.ThresholdHighInput.SuppliedInput.ShouldBe(40);
        actualInputs.MutateInput.SuppliedInput.ShouldHaveSingleItem();
        actualInputs.MutateInput.SuppliedInput.ShouldContain("!**/Test.cs{1..100}{200..300}");
        actualInputs.CoverageAnalysisInput.SuppliedInput.ShouldBe("perTest");
        actualInputs.DisableBailInput.SuppliedInput.ShouldBe(true);
        actualInputs.IgnoreMutationsInput.SuppliedInput.ShouldContain("linq.FirstOrDefault");
        actualInputs.IgnoredMethodsInput.SuppliedInput.ShouldContain("Log*");
        actualInputs.TestCaseFilterInput.SuppliedInput.ShouldBe("(FullyQualifiedName~UnitTest1&TestCategory=CategoryA)|Priority=1");
        actualInputs.DashboardUrlInput.SuppliedInput.ShouldBe("https://alternative-stryker-dashboard.io");
        actualInputs.BreakOnInitialTestFailureInput.SuppliedInput.ShouldNotBeNull().ShouldBeFalse();
    }

    [TestMethod]
    public void WithYamlConfigFile_ShouldStartStrykerWithConfigFileOptions()
    {
        IStrykerInputs actualInputs = null;
        var options = new StrykerOptions()
        {
            Thresholds = new Thresholds()
            {
                High = 80,
                Low = 60,
                Break = 0
            }
        };
        var runResults = new StrykerRunResult(options, 0.3);

        var mock = new Mock<IStrykerRunner>(MockBehavior.Strict);
        mock.Setup(x => x.RunMutationTest(It.IsAny<IStrykerInputs>()))
            .Callback<IStrykerInputs>(c => actualInputs = c)
            .Returns(runResults)
            .Verifiable();

        var target = new StrykerCli(mock.Object, new ConfigBuilder(), Mock.Of<ILoggingInitializer>(), Mock.Of<IStrykerNugetFeedClient>(), Mock.Of<IAnsiConsole>(), Mock.Of<IFileSystem>());

        target.Run(new string[] { "-f", "filled-stryker-config.yaml" });

        mock.VerifyAll();

        actualInputs.AdditionalTimeoutInput.SuppliedInput.ShouldBe(9999);
        actualInputs.VerbosityInput.SuppliedInput.ShouldBe("trace");
        actualInputs.SourceProjectNameInput.SuppliedInput.ShouldBe("ExampleProject.csproj");
        actualInputs.ReportersInput.SuppliedInput.ShouldHaveSingleItem();
        actualInputs.ReportersInput.SuppliedInput.ShouldContain(Reporter.Json.ToString());
        actualInputs.ConcurrencyInput.SuppliedInput.ShouldBe(1);
        actualInputs.ThresholdBreakInput.SuppliedInput.ShouldBe(20);
        actualInputs.ThresholdLowInput.SuppliedInput.ShouldBe(30);
        actualInputs.ThresholdHighInput.SuppliedInput.ShouldBe(40);
        actualInputs.MutateInput.SuppliedInput.ShouldHaveSingleItem();
        actualInputs.MutateInput.SuppliedInput.ShouldContain("!**/Test.cs{1..100}{200..300}");
        actualInputs.CoverageAnalysisInput.SuppliedInput.ShouldBe("perTest");
        actualInputs.DisableBailInput.SuppliedInput.ShouldBe(true);
        actualInputs.IgnoreMutationsInput.SuppliedInput.ShouldContain("linq.FirstOrDefault");
        actualInputs.IgnoredMethodsInput.SuppliedInput.ShouldContain("Log*");
        actualInputs.TestCaseFilterInput.SuppliedInput.ShouldBe("(FullyQualifiedName~UnitTest1&TestCategory=CategoryA)|Priority=1");
        actualInputs.DashboardUrlInput.SuppliedInput.ShouldBe("https://alternative-stryker-dashboard.io");
        actualInputs.BreakOnInitialTestFailureInput.SuppliedInput.ShouldNotBeNull().ShouldBeTrue();
    }
}
